package ru.pb.game.objects.model.players;

import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

import ru.pb.game.configs.PlayerConfig;
import ru.pb.game.dao.PlayerDAO;
import ru.pb.game.data.templates.players.PlayerTemplate;
import ru.pb.game.data.xml.holders.ChannelHolder;
import ru.pb.game.data.xml.holders.PlayerTemplateHolder;
import ru.pb.game.idfactory.IdFactory;
import ru.pb.game.network.clients.GameClient;
import ru.pb.game.network.engine.client.packets.GameServerToClientPacket;
import ru.pb.game.objects.model.GameObject;
import ru.pb.game.objects.model.clan.Clan;
import ru.pb.game.objects.model.inventory.PlayerInventory;
import ru.pb.game.objects.model.players.enums.Team;
import ru.pb.game.objects.model.room.Room;
import ru.pb.game.objects.storage.ObjectsStorage;
import ru.pb.global.dao.DAOManager;
import ru.pb.global.data.model.PlayerInfo;

public class Player extends GameObject
{
	private static final Logger _log = Logger.getLogger(Player.class);
	private static final PlayerDAO _dao = DAOManager.getDAOImpl(PlayerDAO.class);

	public static final int NET_NICK_NAME_SIZE = 33;
	private final ReentrantLock _storeLock = new ReentrantLock();

	private String _accountName;
	private GameClient _connection;
	private boolean _isConnected = true;

	private PlayerInfo _infos;
	private Clan _clan;

	private int _slotId = -1;
	private Room _room;
	private Team _team;

	private PlayerInventory<Player> _inventory;

	public static final byte ONLINE = 1;
	public static final byte OFFLINE = 0;
	private byte _onlineType = OFFLINE;

	public Player(int objectId, String accountName)
	{
		super(objectId);
		_accountName = accountName;
	}

	public static Player create(final GameClient connection, final String name)
	{
		// Дефолтный темплейт из списка в ХМЛ.
		PlayerTemplate template = PlayerTemplateHolder.getInstance().getTemplate(PlayerConfig.DEFAULT_PLAYER_TEMPLATE);
		// Создаем нового плеера
		Player player = new Player(IdFactory.getInstance().getNextId(), connection.getAccount());
		player.setAccount(connection.getAccount());
		connection.setPlayer(player);

		PlayerInfo info = new PlayerInfo();
		info.setRank(template.getRank().ordinal());
		info.setGamePoint(template.getGamePoint());
		info.setMoney(template.getMoney());
		info.setPlayerName(name);

		player.setOnlineStatus(ONLINE);
		player.setPlayerInfos(info);

		// Выдаем стартовые вещи, коорые не нужно одевать.
		for(int itemId : template.getStartArmors().toArray())
			player.getInventory().addItem(itemId, 1);
		for(int itemId : template.getStartWeapons().toArray())
			player.getInventory().addItem(itemId, 1);
		for(int itemId : template.getStartCupons().toArray())
			player.getInventory().addItem(itemId, 1);

		// Выдаем и одеваем стартовые итемы для экипирования.
		for(int itemId : template.getStartEquippedArmors().toArray())
			player.getInventory().equipItem(player.getInventory().addItem(itemId, 1));
		for(int itemId : template.getStartEquippedWeapons().toArray())
			player.getInventory().equipItem(player.getInventory().addItem(itemId, 1));
		for(int itemId : template.getStartEquippedCupons().toArray())
			player.getInventory().equipItem(player.getInventory().addItem(itemId, 1));

		// Add the player in the characters table of the database
		if( !_dao.insert(player))
			return null;
		return player;
	}

	@Override
	public void deleteMe(boolean release)
	{
		setOnlineStatus(OFFLINE);

		try
		{
			getInventory().deleteMe();
		}
		catch(final Throwable t)
		{
			_log.warn("Inventory::deletedMe()", t);
		}

		_inventory = null;

		super.deleteMe(release);
	}

	@Override
	public String toString()
	{
		return getName();
	}

	public ReentrantLock storeLock()
	{
		return _storeLock;
	}

	public byte getOnlineType()
	{
		return _onlineType;
	}

	public boolean isOnline()
	{
		return _onlineType == ONLINE;
	}

	public void setClan(Clan c)
	{
		_clan = c;
	}

	public Clan getClan()
	{
		return _clan;
	}

	public Team getTeam()
	{
		if((_slotId % 2) == 0)
			_team = Team.CHARACTER_TEAM_RED;
		else
			_team = Team.CHARACTER_TEAM_BLUE;
		return _team;
	}

	public PlayerInventory<Player> getInventory()
	{
		if(_inventory == null)
			_inventory = new PlayerInventory<Player>(this);
		return _inventory;
	}

	public void setPlayerInfos(PlayerInfo playerInfo)
	{
		_infos = playerInfo;
	}

	public void incMoney(int toRemove)
	{
		updateMoney(_infos.getMoney() - toRemove);
	}

	public void addMoney(int toAdd)
	{
		updateMoney(_infos.getMoney() + toAdd);
	}

	public void updateMoney(int newMoney)
	{
		_infos.setMoney(newMoney);
		_dao.updateMoney(getAccount(), newMoney, null);
	}

	public PlayerInfo getPlayerInfos()
	{
		return _infos;
	}

	public String getName()
	{
		return _infos.getPlayerName();
	}

	public int getNameColor(boolean canFake)
	{
		return _infos.getNameColor(canFake);
	}

	public String getAccount()
	{
		return _accountName;
	}

	public void setAccount(String acc)
	{
		if(_connection == null)
			_accountName = "<not connected>";

		_accountName = acc;
	}

	public String getIP()
	{
		if(_connection == null)
			return "<not connected>";

		return _connection.getIPString();
	}

	public byte[] getIPBytes()
	{
		return _connection.getIPBytes();
	}

	public byte[] getLocalIp()
	{
		return _connection.getLocalIp();
	}

	public void setOnlineStatus(final byte isOnline)
	{
		_onlineType = isOnline;
		_dao.updateOnline(this);
	}

	public GameClient getClient()
	{
		return _connection;
	}

	public void setClient(final GameClient connection)
	{
		_connection = connection;
	}

	public void close()
	{
		if(_connection != null)
			_connection.close(false);
	}

	public void sendPacket(final Iterable<GameServerToClientPacket> packets)
	{
		if(_isConnected)
		{
			try
			{
				if(_connection != null)
				{
					for(GameServerToClientPacket packet : packets)
						_connection.sendPacket(packet);
				}
			}
			catch(final Exception e)
			{
				_log.info("Problem with send packet " + e, e);
			}
		}
	}

	public void sendPacket(GameServerToClientPacket... packets)
	{
		if(_isConnected)
		{
			try
			{
				if(_connection != null)
				{
					for(GameServerToClientPacket packet : packets)
					{
						if(packet == null)
						{
							_log.info("PACKET = NULL");
							continue;
						}
						_connection.sendPacket(packet);
					}
				}
				else
				{
					_log.info("CONNECTION = NULL");
				}
			}
			catch(final Exception e)
			{
				_log.info("Problem with send packet " + e, e);
			}
		}
	}

	public void broadcastPacket(final GameServerToClientPacket... packets)
	{
		sendPacket(packets);
		broadcastPacketToOthers(packets);
	}

	public void broadcastPacketToOthers(final GameServerToClientPacket... packets)
	{
		int[] list = ChannelHolder.getChannel(getClient().getChannelId()).getAllPlayers().toArray();
		for(int playerObjId : list)
		{
			Player target = ObjectsStorage.getInstance().getPlayer(playerObjId);
			if(target != null)
				target.sendPacket(packets);
		}
	}

	public void setConnected(final boolean connected)
	{
		_isConnected = connected;
	}

	public boolean isConnected()
	{
		return _isConnected;
	}

	public void store(boolean fast, boolean isCreate)
	{
		_dao.update(this, fast, isCreate);
	}

	public void setRoom(Room r)
	{
		_room = r;
	}

	public Room getRoom()
	{
		return _room;
	}

	public void setSlot(int slotId)
	{
		_slotId = slotId;
	}

	public int getSlot()
	{
		return _slotId;
	}

	public boolean isRoomLeader()
	{
		return _room.getLeader() == this;
	}

	private boolean _logoutStarted = false;

	public boolean isLogoutStarted()
	{
		return _logoutStarted;
	}

	public void setLogoutStarted(final boolean logoutStarted)
	{
		_logoutStarted = logoutStarted;
	}

	/**
	 * Сохраняет персонажа в бд и запускает необходимые процедуры.
	 * 
	 * @param shutdown
	 *            тру при шатдауне
	 * @param restart
	 *            тру при рестарте. Игнорируется шатдаун.
	 * @param kicked
	 *            Отобразить у клиента табличку с мессагой о закрытии коннекта
	 */
	public void logout(final boolean shutdown, final boolean restart, final boolean kicked)
	{
		if(isLogoutStarted())
			return;

		setLogoutStarted(true);
		prepareToLogout(kicked);

		_dao.update(this, false, false);

		if(restart)
		{
			// При рестарте просто обнуляем коннект
			if(_connection != null)
				_connection.setPlayer(null);
		}
		else
		{
			if(_connection != null && _connection.getSession() != null)
				_connection.close(true);
		}

		deleteMe(false);

		_connection = null;
		setConnected(false);
	}

	public void prepareToLogout(final boolean kicked)
	{
		if(getRoom() != null)
			getRoom().removePlayer(this);

		if(getClient().getChannelId() >= 0)
			ChannelHolder.getChannel(getClient().getChannelId()).removePlayer(this);
	}
}